﻿#ifndef LOOPBUFFER_H
#define LOOPBUFFER_H

#include <QIODevice>

class QSemaphore;

class LoopBuffer : public QIODevice
{
    Q_OBJECT
public:

    /**
     * 示例:
     * LoopBuffer buf(5);
     * buf.setWriteMode(LoopBuffer::WriteTotalUnlimited);
     * buf.open(QIODevice::ReadWrite);
     *
     * 多线程支持：
     * 多线程模式下使用信号量进行通知，write和read对于信号量对应的字节数值
     *
     * 写入支持三种模式：
     * 写入限制（侧重于提醒写入数据过大）：空间总大小小于一次写入量，写入为0，用户层需要判断写入是否成功（这里并不支持增量写入，因为很多时候上层数据不支持）
     * 写入无限（真正意义的环形缓冲）：待写入值大于预设大小，则会按照“循环覆盖”写入,后面的数据会把前面的数据冲掉（挤掉）
     * 写入余量限制(此模式下准备来说不属于环形缓冲了）：当余量小于待写入大小，返回0。注意：当可用空间不足，而读取又超过的话，会造成读写均不可用！
     */

    //@brief The WriteMode enum
    enum WriteMode{
        WriteTotalLimited,
        WriteTotalUnlimited,// default
        WriteRemainLimited
    };
public:
    static int getAudioSuggestSize(int sample,int channel,int sampleBits);
    LoopBuffer(qint64 size,bool multiThread = false);

    qint64 size() const;
    qint64 usedSize() const;
    qint64 unusedSize() const;

    ~LoopBuffer();
public:
    void setWriteMode(WriteMode mode);
    void setAudioParam(int sampleSize,int channel);
    virtual bool open(OpenMode mode) override;

    void clear();

    /* TODO对于读取过多应该返回错误码 */
    /**
     * @brief readData
     * @param data 用户层分配空间
     * @param maxlen
     * @return
     */
    virtual qint64 readData(char *data, qint64 maxlen) override;
    virtual QByteArray readData(qint64 maxlen);

    /**
     * @brief writeData,两种方式，一次写入总值在一个范围内，一次写入的总值直接覆盖（大于1倍的数据量）
     * @param data
     * @param len greater than 0
     * @return
     */
    virtual qint64 writeData(const char *data, qint64 len) override;

signals:
    void volume(double value);

protected:

private:
    bool m_multiThread = false; /* 支持多线程信号量读取 */

    QSemaphore *m_sem = nullptr;

    QByteArray * m_buffer = nullptr;
    const int m_sizeMax;
    int m_wpos;
    int m_rpos;
    int m_remain;

    int m_sampleBits = 0;
    int m_channel = 0;

    int m_readNullSize = 0;

    WriteMode m_mode = WriteTotalLimited;

    void dumpInfo(const QString &info,QVariant value);
    void boundaryTest();
};

#endif // LOOPBUFFER_H
